package com.zxc.oauth.service.impl

import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.zxc.oauth.exception.OAuthExceptionCode
import com.zxc.oauth.exception.OAuthException
import com.zxc.oauth.model.CurrentAdmin
import com.zxc.oauth.model.OAuthConstant
import com.zxc.oauth.model.OrganizationType
import com.zxc.oauth.model.TokenType
import com.zxc.oauth.properties.OauthAdminProperties
import com.zxc.oauth.service.OauthToken
import com.zxc.oauth.service.TokenService
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.time.Instant

class AdminTokenService(
    private val oauthAdminProperties: OauthAdminProperties
): TokenService<CurrentAdmin> {

    private val logger: Logger = LoggerFactory.getLogger(this::class.java)

    private val jwtHs256 = Algorithm.HMAC256(oauthAdminProperties.accessTokenSecret)

    //可重用的验证程序实例
    private val jwtVerifier = JWT.require(jwtHs256).build()

    override fun createAccessToken(t: CurrentAdmin): OauthToken {
        try {

            val token = JWT.create()
                .withClaim(OAuthConstant.aid, t.id)
                .withClaim(OAuthConstant.name, t.name)
                .withClaim(OAuthConstant.orgType, t.orgType.getValue())
                .withClaim(OAuthConstant.orgId, t.orgId)
                .withClaim(OAuthConstant.exp, Instant.now().plusSeconds(oauthAdminProperties.accessTokenExpiresIn!!).epochSecond)
                .sign(jwtHs256)
            return OauthToken(
                token = token,
                expiresIn = oauthAdminProperties.accessTokenExpiresIn!!
            )
        }catch (e: Exception){
            throw OAuthException(OAuthExceptionCode.LOGIN_ERROR, e)
        }
    }

    override fun createRefreshToken(t: CurrentAdmin): OauthToken {
        try {

            val token = JWT.create()
                .withClaim(OAuthConstant.aid, t.id)
                .withClaim(OAuthConstant.exp, Instant.now().plusSeconds(oauthAdminProperties.refreshTokenExpiresIn!!).epochSecond)
                .sign(jwtHs256)
            return OauthToken(
                token = token,
                expiresIn = oauthAdminProperties.refreshTokenExpiresIn!!
            )
        }catch (e: Exception){
            throw OAuthException(OAuthExceptionCode.LOGIN_ERROR, e)
        }
    }

    override fun accessTokenVerify(content: String): Boolean {
        try {

            val token = getTokenContent(content)
            val jwt = jwtVerifier.verify(token)
            val expire = jwt.getClaim(OAuthConstant.exp).asLong()

            if(expire < Instant.now().epochSecond){
                throw OAuthException(OAuthExceptionCode.ADMIN_AUTH_FAILED)
            }
            return true
        }catch (e: Exception){
            throw OAuthException(OAuthExceptionCode.ADMIN_AUTH_FAILED, e)
        }
    }

    override fun accessTokenDecode(content: String): CurrentAdmin {
        try {

            val token = getTokenContent(content)
            val jwt = jwtVerifier.verify(token)
            val expire = jwt.getClaim(OAuthConstant.exp).asLong()

            if(expire < Instant.now().epochSecond){
                throw OAuthException(OAuthExceptionCode.ADMIN_AUTH_FAILED)
            }
            val adminId = jwt.getClaim(OAuthConstant.aid).asLong()
            val adminName = jwt.getClaim(OAuthConstant.name).asString()
            val orgType = jwt.getClaim(OAuthConstant.orgType).asString()
            val orgId = jwt.getClaim(OAuthConstant.orgId).asLong()

            return CurrentAdmin(
                id = adminId,
                name = adminName,
                orgType = OrganizationType.getInstance(orgType),
                orgId = orgId
            )
        }catch (e: Exception){
            throw OAuthException(OAuthExceptionCode.ADMIN_AUTH_FAILED, e)
        }
    }

    override fun refreshTokenDecode(content: String): CurrentAdmin {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    /**
     * token 的格式 Admin 在外加一个空格字符
     * Admin TOKEN
     */
    private fun getTokenContent(bearerToken: String): String{
        return bearerToken.substring(TokenType.Admin.getValue().length + 1)
    }
}